#include "Player.hpp"
#include "Action.hpp"
#include "Pawn.hpp"
#include "Rook.hpp"
#include "King.hpp"
#include "Queen.hpp"
#include "Bishop.hpp"
#include "Knight.hpp"
#include "CriticalException.hpp"
#include "Position.hpp"

#include "Chess.hpp"

#include <list>
#include <algorithm>
#include <cassert>

#define SET_CASTLING_KING_SIDE_FALSE()		\
		{ 									\
		cstate.king_side = false;			\
		goto checkCastling_queenside;		\
		}


#define SET_CASTLING_QUEEN_SIDE_FALSE()		\
		{									\
		cstate.queen_side = false;			\
		goto checkCastling_end;				\
		}



using namespace std;

struct RecoltPossibleCases : public unary_function<Piece*, void>
{
	list<Position*>& pos;
	
	RecoltPossibleCases(list<Position*>& pos)
		: pos(pos)
	{}
	
	void operator() (Piece* p)
	{
		list<Position*> cases;
		list<Position*>::iterator it;
		
		if (!p->isOut())
		{
			cases = p->getPossibleCases();
			
			for (it = cases.begin() ; it != cases.end() ; it++)
				pos.push_back(*it);
		}
	}
};

struct PieceIsNotOut : public unary_function<Piece*, bool>
{
	PieceIsNotOut()
	{}
	
	bool operator() (Piece* p)
	{		return (!p->isOut());	}
};

struct PieceIsPromotablePawn : public unary_function<Piece*, bool>
{
	PieceIsPromotablePawn()
	{}
	
	bool operator() (Piece* p)
	{
		if (p->getType() != Piece::PAWN)
			return false;
		
		Pawn*	pawn = 0;
		
		assert((pawn = static_cast<Pawn*> (p)) != 0);
		
		return (pawn->isPromotable());
	}
};

struct DelPiecePtrIsEqualTo : public unary_function<Piece*, bool>
{
	Piece* piece;
	
	DelPiecePtrIsEqualTo(Piece* p)
		: piece(p)
	{}
	
	bool operator() (Piece* p)
	{	return (piece == p);	}
};

struct BuildPiecesInfos : public unary_function<Piece*, void>
{
	BuildPiecesInfos(PlayerPiecesInfos* pinfos_)
		: pinfos(pinfos_)
	{}
	
	void operator() (Piece* p)
	{
		if (p->isOut())
			return;
			
		switch(p->getType())
		{
			case	Piece::KING:
				return;
				
			case	Piece::ROOK:
				pinfos->nb_rooks++;
				break;
				
			case	Piece::QUEEN:
				pinfos->nb_queens++;
				break;
				
			case	Piece::KNIGHT:
				pinfos->nb_knights++;
				break;
				
			case	Piece::PAWN:
				pinfos->nb_pawns++;
				break;
				
			case	Piece::BISHOP:
				pinfos->nb_bishops++;
				Bishop* b = 0;
				assert((b = static_cast<Bishop*>(p)) != 0);
				/*
				if ((b = dynamic_cast<Bishop*>(p)) == 0)
				{
					cerr << "bishop dynamic cast failure on BuildPiecesInfos" << endl;
					exit(EXIT_FAILURE);
				}*/
				pinfos->bishops.push_back(b);
				break;
		}
	}
	
	PlayerPiecesInfos* pinfos;
};

	
Player::Player(Chess::Color color)
	: color(color), nb_checks(0), checkmate(false)
{	
	setInitPieces();
	nb_turns = 1;
}

Player::~Player()
{
	try
	{
		
		// 1. delete actions
		list<Action*>::iterator it;
		unsigned int i = 1;
		
		for (; i < this->nb_turns ; i++)
		{
			for (it = actions[i].begin() ; it != actions[i].end() ; it++)
				delete (*it);
		}
		
		actions.clear();
		
		// 2. delete pieces (includes king)
		list<Piece*>::iterator ip;
		Piece*					piece;
		
		for (ip = pieces.begin() ; ip != pieces.end() ; ip++)
		{
			piece = *ip;
			piece->getCell()->setPiece(0);
			piece->setCell(0);
			delete piece;
		}
		
		pieces.clear();
	}
	catch(Exception* e)
	{	throw e;	}
}

list<Position*>	Player::getAllPossiblePositions() const
{
	list<Position*> positions;
	
	for_each(pieces.begin(), pieces.end(), RecoltPossibleCases(positions));
	
	return positions;
}

void	Player::setInitPieces()
{
	unsigned int i;
	
	try
	{
		for (i = 0 ; i < 8 ; i++)
			addPiece(new Pawn(this->color, Position(i, 1, color)));
		
		addPiece(new Rook(this->color, Position(ROOK_INIT_0_X, 0, color)));
		addPiece(new Rook(this->color, Position(ROOK_INIT_1_X, 0, color)));
		addPiece(new Knight(this->color, Position(KNIGHT_INIT_0_X, 0, color)));
		addPiece(new Knight(this->color, Position(KNIGHT_INIT_1_X, 0, color)));
		addPiece(new Bishop(this->color, Position(BISHOP_INIT_0_X, 0, color)));
		addPiece(new Bishop(this->color, Position(BISHOP_INIT_1_X, 0, color)));
		addPiece(new Queen(this->color, Position(((color == Chess::WHITE) ? QUEEN_WHITE_INIT_X : QUEEN_BLACK_INIT_X), 0, color)));
		this->king = new King(this->color, Position(((color == Chess::WHITE) ? KING_WHITE_INIT_X : KING_BLACK_INIT_X), 0, color));
		addPiece(this->king);
	}
	catch(Exception* e)
	{	throw e;	}
}

void	Player::addAction(Action* action)
{
	if (action == 0)
		throw new CriticalException("PLAYER : addAction : action given is NULL !");
	
	actions[Chess::getInstance()->getState().cgs_turn].push_back(action);
}

const list<Piece*>&	Player::getPieces() const
{	return pieces;	}


void	Player::getPlayerPiecesInfos(PlayerPiecesInfos* pieces_infos)
{	for_each(pieces.begin(), pieces.end(), BuildPiecesInfos(pieces_infos));	}

King*	Player::getKing() const
{	return (king);	}

void			Player::incrChecks()
{	nb_checks++;	}

void			Player::incrNbTurns()
{	nb_turns++;	}

unsigned int	Player::getNbChecks() const
{	return nb_checks;	}


unsigned int	Player::getNbPieces() const
{	return (pieces.size());	}

unsigned int	Player::getNbTurns() const
{	return (nb_turns);	}

Piece*	Player::checkPromotablePawn()	const
{
	list<Piece*>::const_iterator it;
	
	if ((it = find_if(pieces.begin(), pieces.end(), PieceIsPromotablePawn())) == pieces.end())
		return 0;
	else
		return (*it);
}

CastlingState		Player::checkCastling() const
{
	try
	{	
		Chessboard*		cboard = Chess::getInstance()->getChessboard();
		CastlingState	cstate;
		Piece*	r = 0;
		Piece*	k = 0;
		unsigned int i = 0;
		unsigned int king_x;
		unsigned int rook_x = 0;
		unsigned int dep_x = 0;
		unsigned int arr_x = 0;
		
		king_x = (this->color == Chess::WHITE) ? KING_WHITE_INIT_X : KING_BLACK_INIT_X;
		
		
		{
			rook_x = (this->color == Chess::WHITE) ? ROOK_INIT_1_X : ROOK_INIT_0_X;
			
			// 1. checking positions
			if ((r = cboard->getCell(Position(rook_x, 0, this->color))->getPiece()) == 0)
				SET_CASTLING_KING_SIDE_FALSE();
			
			
			if ((k = cboard->getCell(Position(king_x, 0, this->color))->getPiece()) == 0)
					SET_CASTLING_KING_SIDE_FALSE();	
			
			// 2. checking movements counts
			// don't have to check piece type, because if piece has no movement
			// then it corresponds to the good one
			if (!((r->getNbMovements() == 0) && (k->getNbMovements() == 0)))
				SET_CASTLING_KING_SIDE_FALSE();
			
			dep_x = (this->color == Chess::WHITE) ? (king_x + 1) : (rook_x + 1);
			arr_x = (this->color == Chess::WHITE) ? (rook_x) : (king_x);
			
			// 3. checking cells between rook & knight, must be empty !
			for (i = dep_x ; i < arr_x ; i++)
			{
				if (cboard->getCell(Position(i, 0, this->color))->hasPiece())
					SET_CASTLING_KING_SIDE_FALSE();
			}
			
			cstate.king_side = true;
		}
		// checking castling queenside
		checkCastling_queenside:
		{
			rook_x = (this->color == Chess::WHITE) ? ROOK_INIT_0_X : ROOK_INIT_1_X;
			
			// 1. checking positions
			if ((r = cboard->getCell(Position(rook_x, 0, this->color))->getPiece()) == 0)
				SET_CASTLING_QUEEN_SIDE_FALSE();
			
			if ((k = cboard->getCell(Position(king_x, 0, this->color))->getPiece()) == 0)
				SET_CASTLING_QUEEN_SIDE_FALSE();
			
			// 2. checking movements counts
			if (!((r->getNbMovements() == 0) && (k->getNbMovements() == 0)))
				SET_CASTLING_QUEEN_SIDE_FALSE();
			
			dep_x = (this->color == Chess::WHITE) ? (rook_x + 1) : (king_x + 1);
			arr_x = (this->color == Chess::WHITE) ? (king_x) : (rook_x);
			
			// 3. checking cells between rook & knight, must be empty !
			for (i = dep_x ; i < arr_x ; i++)
			{
				if (cboard->getCell(Position(i, 0, this->color))->hasPiece())
					SET_CASTLING_QUEEN_SIDE_FALSE();
			}
			
			cstate.queen_side = true;
		}
		checkCastling_end:
		{}
		
		return (cstate);
		
	}
	catch (Exception* e)
	{
		throw e;
	}
}

void		Player::displayDbg()
{
	/*list<Piece*>::iterator it;
	
	for (it = pieces.begin() ; it != pieces.end() ; it++)
	{
		cout << hex << *it << " ";
	}
	cout << endl;*/
}


void		Player::addPiece(Piece* p)
{
	assert(p != 0);	
	this->pieces.push_back(p);
}

void		Player::removePiece(Piece* piece)
{	
	assert(piece != 0);
	
	list<Piece*>::iterator it;
	
	for (it = pieces.begin() ; it != pieces.end() ; it++)
	{
		if ((*it) == piece)
			break;
	}
	
	delete (*it);	
	pieces.erase(it);
}

const map<unsigned int, list<Action*> >& Player::getActions() const
{	return	(this->actions);	}

Chess::Color Player::getColor() const
{	return (color);		}
